package com.ymttest.utils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.junit.Ignore;

import com.google.gson.internal.LinkedTreeMap;
import com.google.gson.reflect.TypeToken;
import com.ymt.core.tool.FormatUtil;
import com.ymt.core.tool.Logger;

public class DiffUtil {
	private List<String> ignoreList = new ArrayList<>();
	private String outfile;
	private boolean isTrim = true;
	private boolean identical = true;
	private Map<String, String> precision = new HashMap<>();

	/**
	 * 专门用于gson对比 LinkedTreeMap2TreeMap2String2compare
	 * 
	 * @param obj1
	 * @param obj2
	 * @return
	 */
	private int compare2(Object obj1, Object obj2) {
		if (obj1.getClass().getSimpleName().equals("LinkedTreeMap")) {
			TreeMap<String, Object> m1 = new TreeMap<>((LinkedTreeMap) obj1);
			TreeMap<String, Object> m2 = new TreeMap<>((LinkedTreeMap) obj2);
			return m2.toString().compareTo(m1.toString());
		}
		String s1 = FormatUtil.beanToGSONString(obj1).toString();
		String s2 = FormatUtil.beanToGSONString(obj2).toString();
		// 降序排序
		return s2.compareTo(s1);
	}

	/**
	 * 对比2个lint区别 GSON专用
	 * 
	 * @param oldlistvalue
	 * @param newlistvalue
	 * @param pk
	 *            路径 Data.PromotionInfo.PromotionValues.AmountCondition
	 * @param outpk
	 *            显示路径 Data.PromotionInfo.PromotionValues_0.AmountCondition
	 */
	public void compareList(List oldlistvalue, List newlistvalue, Object pk, Object outpk) {
		oldlistvalue.sort(new Comparator<Object>() {
			public int compare(Object obj1, Object obj2) {
				return compare2(obj1, obj2);
			}
		});
		newlistvalue.sort(new Comparator<Object>() {
			public int compare(Object obj1, Object obj2) {
				return compare2(obj1, obj2);
			}
		});
		String tmpk = pk.toString();
		if (oldlistvalue.size() == newlistvalue.size()) {
			for (int i = 0; i < oldlistvalue.size(); i++) {
				// 如果是LinkedTreeMap
				if (oldlistvalue.get(i).getClass().getSimpleName().equals("LinkedTreeMap")) {
					TreeMap<String, Object> m1 = new TreeMap<>((LinkedTreeMap) oldlistvalue.get(i));
					TreeMap<String, Object> m2 = new TreeMap<>((LinkedTreeMap) newlistvalue.get(i));
					compareTreeMap(m1, m2, tmpk, outpk + "_" + i);
				}
				// 理论上格式正确的话不会走到这里
				else if (oldlistvalue.get(i).getClass().getSimpleName().equals("ArrayList")) {
					List tol = (List) oldlistvalue.get(i);
					List tnl = (List) newlistvalue.get(i);
					compareList(tol, tnl, tmpk, outpk + "_" + i);
				} else if (!oldlistvalue.get(i).equals(newlistvalue.get(i))) {
					// list的显示名加上_index
					execLog(oldlistvalue.get(i), newlistvalue.get(i), tmpk, outpk + "_" + i);
				}
			}
		} else {
			// list的size不一致时候显示名加上_size
			execLog(oldlistvalue.size(), newlistvalue.size(), tmpk, outpk + "_size");
		}
	}

	/**
	 * 对比2个map区别 GSON专用
	 * 
	 * @param oldmap
	 * @param newmap
	 * @param pk
	 *            路径 Data.PromotionInfo.PromotionValues.AmountCondition
	 * @param outpk
	 *            显示路径 Data.PromotionInfo.PromotionValues_0.AmountCondition
	 */

	public void compareTreeMap(TreeMap<String, Object> oldmap, TreeMap<String, Object> newmap, String pk, String outpk) {
		Iterator iter1 = oldmap.entrySet().iterator();
		// 遍历oldmap
		while (iter1.hasNext()) {
			Map.Entry entry1 = (Map.Entry) iter1.next();
			Object m1value = entry1.getValue() == null ? "" : entry1.getValue();
			// 重置key,基础为父节点pk
			String tmpkey = "";
			// outpk为输出显示名
			String tmpoutkey = "";
			if (pk.length() > 0) {
				tmpkey = pk + "." + entry1.getKey();
				tmpoutkey = outpk + "." + entry1.getKey();
			} else {
				tmpkey = entry1.getKey().toString();
				tmpoutkey = entry1.getKey().toString();
			}
			// 通过oldmap的entry1.getKey() 获取new map 的对应值
			Object m2value = newmap.get(entry1.getKey()) == null ? "" : newmap.get(entry1.getKey());
			// 如果是list 调用compareList
			if (m1value.getClass().getSimpleName().equals("ArrayList")) {
				try {
					List oldlistvalue = (List) m1value;
					List newlistvalue = (List) m2value;
					compareList(oldlistvalue, newlistvalue, tmpkey, tmpoutkey);
				} catch (Exception e) {
					execLog(m1value, m2value, tmpkey, tmpoutkey);
				}

			}
			// 如果是LinkedTreeMap 调用compareTreeMap
			else if (m1value.getClass().getSimpleName().equals("LinkedTreeMap")) {
				LinkedTreeMap ltm1 = (LinkedTreeMap) m1value;
				LinkedTreeMap ltm2 = null;
				if (m2value != null) {
					ltm2 = (LinkedTreeMap) m2value;
				} else {
					execLog(m1value, m2value, tmpkey, tmpoutkey);
				}
				TreeMap<String, Object> tm1 = new TreeMap<>(ltm1);
				TreeMap<String, Object> tm2 = new TreeMap<>(ltm2);
				compareTreeMap(tm1, tm2, tmpkey, tmpoutkey);
			}
			// 以上都不是 若两个map中相同key对应的value不相等
			else if (!m1value.equals(m2value)) {
				execLog(m1value, m2value, tmpkey, tmpoutkey);
			}
		}
	}

	/**
	 * 执行过滤并输出
	 * 
	 * @param oldv
	 * @param newv
	 * @param key
	 * @param outkeyname
	 */
	public void execLog(Object oldv, Object newv, String key, String outkeyname) {
		// 忽略列表
		if (!ignoreList.contains(key)) {
			if (precision.get(key) != null) {
				try {
					String otemp = TestDataManager.getRegexList(oldv.toString(), precision.get(key)).get(0);
					String ntemp = TestDataManager.getRegexList(newv.toString(), precision.get(key)).get(0);
					oldv = otemp;
					newv = ntemp;
				} catch (Throwable e) {
					e.printStackTrace();
				}
			}
			// isTrim 会过滤首尾空格
			if (isTrim) {
				if (!oldv.toString().trim().equals(newv.toString().trim().toString())) {
					outDiffLog(oldv, newv, key, outkeyname);
				}
			} else {
				outDiffLog(oldv, newv, key, outkeyname);
			}
		}
	}

	/**
	 * 输出有区别的对比结果
	 * 
	 * @param oldv
	 * @param newv
	 * @param key
	 * @param outkeyname
	 */
	public void outDiffLog(Object oldv, Object newv, String key, String outkeyname) {
		if (myRule(oldv, newv, key, outkeyname)) {
			Logger.debug(outkeyname + ":OLDVALUE:" + oldv.toString() + ":NEWVALUE:" + newv.toString());
			TestDataManager.appendFileToLine(outfile, outkeyname + ":OLDVALUE:" + oldv.toString() + ":NEWVALUE:" + newv.toString());
			identical = false;
		}
	}

	/**
	 * 设置对比规则
	 * 
	 * @param m
	 *            <br>
	 *            key为字段
	 *            例如Data.PromotionInfo.PromotionValues.AmountCondition<br>
	 *            value为正则表达式
	 */
	public void setPrecision(Map<String, String> m) {
		precision = m;
	}

	/**
	 * 对比是是否去除首尾空白符,默认去除
	 * 
	 * @param istrim
	 */
	public void isTrim(Boolean istrim) {
		this.isTrim = istrim;
	}

	/**
	 * 设置输出文件
	 * 
	 * @param filename
	 */
	public void setLogFile(String filename) {
		this.outfile = filename;
	}

	/**
	 * 日志文件追加记录
	 * 
	 * @param msg
	 */
	public void appendLogFile(String msg) {
		if (outfile != null && outfile.length() > 0) {
			TestDataManager.appendFileToLine(outfile, msg);
		}
	}

	/**
	 * 对比2个json区别
	 * 
	 * @param oldjson
	 * @param newjson
	 */
	public boolean compareJsonString(String oldjson, String newjson) {
		// 转换成treemap
		TreeMap<String, Object> oldmap = (TreeMap<String, Object>) FormatUtil.GSONToObject(oldjson, new TypeToken<TreeMap<String, Object>>() {
		}.getType());
		TreeMap<String, Object> newmap = (TreeMap<String, Object>) FormatUtil.GSONToObject(newjson, new TypeToken<TreeMap<String, Object>>() {
		}.getType());
		compareTreeMap(oldmap, newmap, "", "");
		return identical;
	}

	/**
	 * 设置忽略对比字段 例如 Data.PromotionProductCountInfo
	 * 
	 * @param list
	 */
	public void setIgnoreList(List<String> list) {
		this.ignoreList = list;
	}

	/**
	 * 自定义规则 根据情况修改<br>
	 * 默认进入这里的都是有差异的
	 * 
	 * @param oldv
	 * @param newv
	 * @param key
	 * @param outkeyname
	 * @return true 输出差异 false 忽略差异
	 */
	public boolean myRule(Object oldv, Object newv, String key, String outkeyname) {
		return true;
	}

	public static void main(String args[]) {
//		String s1= TestDataManager.getAllLines("1.json");
//		String s2= TestDataManager.getAllLines("2.json");
//		new DiffUtil().compareJsonString(s2.toString(), s1.toString());
		ArrayList<String> ss1 = TestDataManager.getLines("1.txt");
		ArrayList<String> ss2 = TestDataManager.getLines("tododomain.txt");
		ss2.forEach(x->{
			if(!ss1.contains(x)) {
				System.out.println(x);
			}
		});
	}
}
